#include "spi.h"

#include "delay.h"


/**
 @brief 初始化w25q128闪存
*/
void w25q128_init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	SPI_InitTypeDef  SPI_InitStructure;
	
	//使能端口B的硬件时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);	
	
	//使能SPI1的硬件时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
	
	
	//配置PB3~PB5为复用功能模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;	//指定3、4、5号引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//配置为复用功能模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高，功耗就越高，但是响应速度也更快
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻
	GPIO_Init(GPIOB, &GPIO_InitStructure);	
	
	
	//将PB3~PB5连接到SPI1的硬件
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1);
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1);	
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1);		
	
	
	//配置PB14为输出模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;	//指定14号引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//配置为推挽功能模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高，功耗就越高，但是响应速度也更快
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻
	GPIO_Init(GPIOB, &GPIO_InitStructure);	
	
	//PB14初始电平状态为高电平，因为总线空闲的时候，SS引脚（片选引脚）为高电平
	PBout(14)=1;
	
	
	//SPI1参数的配置
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//主机模式
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;//每次传输最小单元为字节
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//根据从机的手册来配置，模式3
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//根据从机的手册来配置，模式3
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//由软件代码控制片选引脚
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;//根据从机的手册来配置，SPI的硬件时钟=84MHz/6=5.25MHz
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;//根据从机的手册来配置
	SPI_Init(SPI1, &SPI_InitStructure);
	
	
	//使能SPI1硬件工作
	SPI_Cmd(SPI1,ENABLE);
}


/**
 @brief 使用引脚模拟硬件闪存初始化
 @note 使用的原因是,由于有些硬件没有SPI,只能由引脚模拟
*/
void w25q128_simulate_init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	
	//使能端口B的硬件时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);	
	
	//配置PB3 PB5 PB14为复用功能模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_5|GPIO_Pin_14;	//指定3、5、14号引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//配置为输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高，功耗就越高，但是响应速度也更快
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻
	GPIO_Init(GPIOB, &GPIO_InitStructure);	
	
	//配置PB4为输入模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;	//指定4号引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//配置为输入模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高，功耗就越高，但是响应速度也更快
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻
	GPIO_Init(GPIOB, &GPIO_InitStructure);	
	
	//PB14初始电平状态为高电平，因为总线空闲的时候，SS引脚（片选引脚）为高电平
	W25Q128_SS=1;
	
	//CPOL=1,SCLK引脚在SPI总线空闲的时候为高电平
	W25Q128_SCLK=1;
	
	//MOSI引脚随意电平
	W25Q128_MOSI=1;
}
	


/**
 @brief 向SPI1发送字节
 @param byte: 要发送的字节
*/
uint8_t SPI1_SendByte(uint8_t byte)
{
    /*!< Loop while DR register in not emplty */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);

    /*!< Send byte through the SPI1 peripheral */
    SPI_I2S_SendData(SPI1, byte);

    /*!< Wait to receive a byte */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

    /*!< Return the byte read from the SPI bus */
    return SPI_I2S_ReceiveData(SPI1);
}


/**
 @brief 使用引脚模拟,向SPI1发送字节,与w25q128_simulate_init一起用
 @param byte: 要发送的字节
*/
uint8_t SPI1_Simulate_SendByte(uint8_t byte)
{
	int32_t i = 0;
	uint8_t d = 0;
	
	for(i=7; i>=0; i--){
		if(byte & (1<<i))
			W25Q128_MOSI=1;
		else
			W25Q128_MOSI=0;
		
		W25Q128_SCLK=0;
		delay_us(1);
		
		W25Q128_SCLK=1;
		delay_us(1);
		
		if(W25Q128_MISO)
			d |= 1<<i;
	}
	
	return d;
}


/**
 @brief 读取厂商和设备id
 @param m_id: 厂商id (m_id=EF)
 @param d_id: 设备id (d_id=17)
*/
void w25q128_read_id(uint8_t *m_id,uint8_t *d_id)
{
	//片选引脚拉低
	W25Q128_SS=0;

	//发送0x90
	SPI1_SendByte(0x90);

	//发送24bit的地址，全为0
	SPI1_SendByte(0x00);
	SPI1_SendByte(0x00);
	SPI1_SendByte(0x00);

	//读取厂商id，参数可以为任意参数
	*m_id=SPI1_SendByte(0xFF);
	//读取设备id
	*d_id=SPI1_SendByte(0xFF);

	//片选引脚拉高
	W25Q128_SS=1;	
}


/**
 @brief 读取厂商和设备id
 @param m_id: 厂商id (m_id=EF)
 @param d_id: 设备id (d_id=17)
*/
void w25q128_simulate_read_id(uint8_t *m_id,uint8_t *d_id)
{
	//片选引脚拉低
	W25Q128_SS=0;

	//发送0x90
	SPI1_Simulate_SendByte(0x90);

	//发送24bit的地址，全为0
	SPI1_Simulate_SendByte(0x00);
	SPI1_Simulate_SendByte(0x00);
	SPI1_Simulate_SendByte(0x00);

	//读取厂商id，参数可以为任意参数
	*m_id=SPI1_Simulate_SendByte(0xFF);
	//读取设备id
	*d_id=SPI1_Simulate_SendByte(0xFF);

	//片选引脚拉高
	W25Q128_SS=1;	
}



/**
  * @brief  Reads a byte from the SPI Flash.
  * @note   This function must be used only if the Start_Read_Sequence function
  *         has been previously called.
  * @param  None
  * @retval Byte Read from the SPI Flash.
  */
uint8_t w25q128_read_byte(uint32_t addr)
{
	uint8_t p;
	
	//片选引脚拉低
	W25Q128_SS=0;

	//发送0x03,根据时序图决定
	SPI1_SendByte(0x03);

	//发送24bit的地址
	SPI1_SendByte(addr >> 16);
	SPI1_SendByte(addr >> 8);
	SPI1_SendByte(addr);
	
	p = SPI1_SendByte(0xFF);
    
	//片选引脚拉高
	W25Q128_SS=1;
	
	return p;
}


/**
  * @brief  Reads a byte from the SPI Flash.
  * @note   This function must be used only if the Start_Read_Sequence function
  *         has been previously called.
  * @param  addr: 地址
  * @retval Byte Read from the SPI Flash.
  */
uint8_t w25q128_simulate_read_byte(uint32_t addr)
{
	uint8_t p;
	
	//片选引脚拉低
	W25Q128_SS=0;

	//发送0x03,根据时序图决定
	SPI1_Simulate_SendByte(0x03);

	//发送24bit的地址
	SPI1_Simulate_SendByte(addr >> 16);
	SPI1_Simulate_SendByte(addr >> 8);
	SPI1_Simulate_SendByte(addr);
	
	p = SPI1_Simulate_SendByte(0xFF);
    
	//片选引脚拉高
	W25Q128_SS=1;
	
	return p;
}


/**
 @brief 从指定地址读取指定长度字节
 @param addr: 地址
 @param buf: 用于保存数据
 @param len: 读取的长度
*/
void w25q128_read_buf(uint32_t addr, uint8_t *buf, uint32_t len)
{
	uint8_t *p = buf;
	
	//片选引脚拉低
	W25Q128_SS=0;

	//发送0x03,根据
	SPI1_SendByte(0x03);

	//发送24bit的地址
	SPI1_SendByte(addr >> 16);
	SPI1_SendByte(addr >> 8);
	SPI1_SendByte(addr);

	while(len--){
		*p = SPI1_SendByte(0xFF);
		p++;
	}

	//片选引脚拉高
	W25Q128_SS=1;	
}

/**
 @brief 从指定地址读取指定长度字节
 @param addr: 地址
 @param buf: 用于保存数据
 @param len: 读取的长度
*/
void w25q128_simulate_read_buf(uint32_t addr, uint8_t *buf, uint32_t len)
{
	uint8_t *p = buf;
	
	//片选引脚拉低
	W25Q128_SS=0;

	//发送0x03,根据
	SPI1_SendByte(0x03);

	//发送24bit的地址
	SPI1_Simulate_SendByte(addr >> 16);
	SPI1_Simulate_SendByte(addr >> 8);
	SPI1_Simulate_SendByte(addr);

	while(len--){
		*p = SPI1_Simulate_SendByte(0xFF);
		p++;
	}

	//片选引脚拉高
	W25Q128_SS=1;	
}

